﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using DAL;
using BusinessEntities.Model;
using BusinessEntities;
using System.Configuration;
namespace BusinessLayer
{
    public class LocalBusinessLayer : IBL
    {
        #region Private Memebers
        private IMailer _mailer;
        private IDAL _dal;
        private string domainName;
        #endregion

        #region Constructor
        public LocalBusinessLayer()
        {
            _dal = new DalImpl();
            _mailer = new LipkinFamilyMailer();
            this.domainName = System.Configuration.ConfigurationManager.AppSettings["DomainName"] ?? "yems.somee.com";
        }

        public LocalBusinessLayer(string domainName)
        {
            _dal = new DalImpl();
            _mailer = new LipkinFamilyMailer();
            this.domainName = domainName;
        }
        #endregion

        #region User Registration
        
        public void ResendActivationEmail(int userid)
        {
            User u = _dal.GetUserById(userid);
            if (u != null && !u.Activated)
            {
                try
                {
                    SendActivationMail(u);
                }
                catch (Exception) { }
            }
        }

        public ModelValidationReport<BusinessEntities.Model.User> LoginUser(string email, string password)
        {
            BusinessEntities.Model.User user = _dal.GetUserByEmail(email);
            ModelValidationReport<User> report = new ModelValidationReport<User>(user);
            if (user != null && user.Password == password && user.Activated)
            {
                report.Valid = true;
            }
            else if (user == null || user.Password != password)
            {
                report.Valid = false;
                report.Message = "There was an error with the email/password combination, try again.";
            }
            else if(!user.Activated)
            {
                report.Message = @"You have yet to activate your account...<br/>If you deleted
                                   your email, you can click <a href=""http://" + this.domainName + "/SendMail.aspx?id=" +
                                                                                user.ID.ToString() + @""">here</a>
                                   to get a new one.";
                report.Valid = false;
              
            }
            return report;
        }

        public string CreateActivationHash(BusinessEntities.Model.User user)
        {
            user.Activated = false;
            _dal.CreateUser(user);
            return user.CreateHash();
        }        

        public User ValidateActivationHash(string hash, int userId)
        {
            User user = _dal.GetUserById(userId);
            if (user == null)
                return null;
            if (user.CreateHash() == hash)
            {
                user.Activated = true;
                _dal.UpdateUser(user);
            }
            else
            {
                return null;
            }
            return user;
        }

        public void SendMail(System.Net.Mail.MailMessage message)
        {

            _mailer.SendMail(message);
        }

        private void SendActivationMail(User user)
        {
            System.Net.Mail.MailMessage message = new System.Net.Mail.MailMessage("admin@yems.somee.com",user.Email);
            message.IsBodyHtml = true;
            message.Body = @"
            Hi " + user.FirstName + @"! Thanks for signing up for YorKin Events!
            <br/>
            We just need to confirm that this is your email :) 
            <br/>
            All you gotta do is click here:<br/>http://" + this.domainName + "/ConfirmEmail.aspx?id=" + user.ID.ToString() + @"&h=" + user.CreateHash() + @"
            <br/><br/><br/>
            YorKin Events
            ";
            message.Subject = "Activate your YorKin account!";
            SendMail(message);
        }

        public  ModelValidationReport<User> CreateUser(User user)
        {
            var report = ModelValidation.ValidateUser(user);
            if (!report.Valid)
            {
                return report;
            }
            User retUser = _dal.CreateUser(user);
            
            try
            {
                SendActivationMail(user);
                report.ModelObject = retUser;
            }
            catch (Exception)
            {
                _dal.DeleteUser(user);
                report.Valid = false;
                report.Message += "\nAn activation email could not be sent.";
            }
            return report;
        }


        public User GetUserByID(int id)
        {
            return _dal.GetUserById(id);
        }


        public User GetUserByEmail(string email)
        {
            return _dal.GetUserByEmail(email);
        }
        #endregion

        public IEnumerable<Hall> GetAllHalls()
        {
            return _dal.GetAllHalls();
        }


        public Hall GetHallById(int id)
        {
            return _dal.GetAllHalls().Where(h => h.ID == id).FirstOrDefault();
        }


        public  ModelValidationReport<Event> CreateEvent(Event aEvent)
        {
            var report = ModelValidation.ValidateEvent(aEvent);
            if (!report.Valid)
            {
                return report;                
            }
            report.ModelObject = _dal.CreateEvent(aEvent);
            return report;
        }


        public Event GetEventById(int id)
        {
            return _dal.GetEventById(id);
        }


        public IEnumerable<Event> GetUsersEvents(User user)
        {
            return _dal.GetUsersEvents(user);
        }


        public EventReport GetEventReport(Event aEvent)
        {
            var invites = _dal.GetEventsInvites(aEvent);
            var evReport = new EventReport();
            evReport.TotalGuestsRSVPd = invites.
                Where(i => i.RSVPd).
                Select(i => i.AdditionalInvitedGuests + (i.Guest.Couple ? 2 : 1)).
                Sum();
            evReport.TotalGuestsComing = invites
                .Where(i => i.RSVPd)
                .Select(i => i.ReservedGuests)
                .Sum();
            evReport.TotalGuestsInvited = invites
                .Select(i => i.AdditionalInvitedGuests + (i.Guest.Couple ? 2 : 1))
                .Sum();
            evReport.Event = aEvent;
            return evReport;
        }


        public void DeleteEvent(Event aEvent)
        {
            _dal.DeleteEvent(aEvent);
        }


        public ModelValidationReport<Event> UpdateEvent(Event _event)
        {
            var report = ModelValidation.ValidateEvent(_event);
            if (!report.Valid)
            {
                return report;
            }
            report.ModelObject = _dal.UpdateEvent(_event);
            return report;
        }


        public IEnumerable<City> GetCitiesByKeyWord(string keyWord)
        {
            return _dal.GetCitiesByKeyword(keyWord);
        }


        public City GetCityByName(string name)
        {
            return _dal.GetCityByName(name);
        }


        public City GetCityByID(int id)
        {
            return _dal.GetAllCities().Where(c => c.ID == id).FirstOrDefault();
        }


        public Guest GetUsersGuestByEmail(string email, int userid)
        {
            return _dal.GetUsersGuestByEmail(email, userid);
        }


        public ModelValidationReport<Guest> CreateGuest(Guest guest)
        {
            var report = ModelValidation.ValidateGuest(guest);
            if (!report.Valid)
            {
                return report;
            }
            report.ModelObject = _dal.CreateGuest(guest);
            return report;
        }


        public IEnumerable<Guest> GetUsersGuests(User user)
        {
            var ret = _dal.GetUsersGuests(user);
            foreach (var guest in ret)
            {
                guest.User = null;
            }
            return ret;
        }


        public Guest GetGuestByID(int id)
        {
            return _dal.GetGuestByID(id);
        }


        public IEnumerable<Invite> GetGuestsInvites(Guest g)
        {
            var ret = _dal.GetGuestsInvites(g);
            foreach (var invite in ret)
            {
                invite.Guest.User = null;
                invite.Event.User = null;
            }
            return ret;
        }


        public IEnumerable<Guest> SearchForGuests(string keyword, int userid)
        {
            IEnumerable<Guest> ret = _dal.SearchForGuests(keyword, userid);
            foreach (var item in ret)
            {
                item.User = null;
            }
            return ret;
        }


        public IEnumerable<Guest> SearchForNonInvitedGuests(string keyword, int eventID, int userid)
        {
            var ev = _dal.GetEventById(eventID);
            if (ev.UserID != userid)
            {
                return null;
            }
            var ret = (from i in _dal.GetEventsInvites(ev)
                       select i.GuestID);
            var r = (from g in _dal.SearchForGuests(keyword,userid)
                     where !ret.Contains(g.ID)
                     select g);
            foreach (var guest in r)
            {
                guest.User = null;
            }
            return r;
        }


        public ModelValidationReport<Invite> CreateInvite(Invite invite)
        {
            ModelValidationReport<Invite> report;
            var retinvite = _dal.GetEventsInvites(_dal.GetEventById(invite.EventID))
                .Where(i => i.GuestID == invite.GuestID)
                .FirstOrDefault();
            if (retinvite == null)
            {
                report = ModelValidation.ValidateInvite(invite);
                if (!report.Valid)
                {
                    return report;
                }
                string toHash = Guid.NewGuid().ToString();
                MD5 md5 = MD5.Create();
                StringBuilder hash = new StringBuilder();
                foreach (byte b in md5.ComputeHash(Encoding.ASCII.GetBytes(toHash)))
                {
                    hash.Append(b.ToString("x2").ToLower());
                }
                invite.Hash = hash.ToString();
                report.ModelObject = _dal.CreateInvite(invite);
            }
            else
            {
                report = new ModelValidationReport<Invite>(invite, false, "That guest is already invited.");
            }
            return report;
        }


        public IEnumerable<Invite> GetInvitesByEvent(Event ev)
        {
            return _dal.GetEventsInvites(ev);
        }


        public Invite GetInviteByEventAndGuest(Event ev, Guest guest)
        {
            return _dal.GetEventsInvites(ev)
                .Where(i => i.GuestID == guest.ID)
                .FirstOrDefault();
        }


        public ModelValidationReport<Invite> UpdateInvite(Invite invite)
        {
            var report = ModelValidation.ValidateInvite(invite);
            if (!report.Valid)
            {
                return report;
            }
            report.ModelObject = _dal.UpdateInvite(invite);
            return report;
        }


        public Invite GetInviteById(int id)
        {
            return _dal.GetAllInvites().Where(i => i.ID == id).FirstOrDefault();
        }


        public Invite GetInviteByHash(string hash,int guestID)
        {
            Guest g = _dal.GetGuestByID(guestID);
            if (g == null)
            {
                return null;
            }
            return _dal.GetGuestsInvites(g).Where(i => i.Hash == hash).FirstOrDefault();
        }


        public bool DeleteInvite(int eventID, int inviteID, int guestID)
        {
            var inv = _dal.GetAllInvites().Where(i => i.ID == inviteID).FirstOrDefault();
            if (inv != null && inv.EventID == eventID && inv.GuestID == guestID)
            {
                _dal.DeleteInvite(inv);
                return true;
            } 
            return false;           
        }

        public decimal GetAverageGuestGifts(Guest guest)
        {
            var invites = GetGuestsInvites(guest);
            decimal sum = 0;
            int count = 0;
            foreach (var invite in invites)
            {
                if (invite.Gift != null && (invite.Gift is Cash || invite.Gift is Check))
                {
                    sum += (decimal)invite.Gift.GetType().GetProperty("Amount").GetValue(invite.Gift);
                    count++;
                }
            }
           /* decimal avg = invites.ToList().
                Where(i => i.Gift.GetType().Name == "Cash" || i.Gift.GetType().Name == "Check").
                Average(i => (decimal)i.Gift.GetType().GetProperty("Amount").GetValue(i));*/
            return count == 0 ? 0 : sum / count;
        }


        public List<User> GetAllUsers()
        {
            return _dal.GetAllUsers().ToList();
        }


        public void DeleteUser(User user)
        {
            _dal.DeleteUser(user);
        }


        public ModelValidationReport<User> UpdateUser(User user)
        {
            var report = ModelValidation.ValidateUser(user);
            if (report.Valid)
            {
                report.ModelObject = _dal.UpdateUser(user);
            }
            return report;
        }
    }
}
